﻿using DynamicAuthorization.Sdk.Abstarctions;
using DynamicAuthorization.Sdk.Attributes;
using System.Reflection;

namespace DynamicAuthorization.Sdk.Serialization;

public class GrantTypeSerialization
{
    public IEnumerable<string> Modules { get; set; } = new HashSet<string>();

    public ICollection<GrantMethodSerialization> Functions { get; set; } = new HashSet<GrantMethodSerialization>();

    public IEnumerable<string> Routers { get; set; } = Enumerable.Empty<string>();

    public IEnumerable<string> RejectModules { get; set; } = Enumerable.Empty<string>();
}

public class GrantMethodSerialization
{
    public string BelongModule { get; set; } = null!;

    public IEnumerable<string> Functions { get; set; } = Enumerable.Empty<string>();

    public IEnumerable<string> RejectFunctions { get; set; } = Enumerable.Empty<string>();
}

public static class GrantTypeMetadataExtensions
{
    public static GrantTypeSerialization ToSerialization(this IGrantTypeMetadata metadata)
    {
        var seria = new GrantTypeSerialization
        {
            Modules = metadata.GrantClasses.Except(metadata.RejectClasses).Select(NameSelector).ToArray()
        };

        if (metadata.IsAdmin)
        {
            seria.Functions.Add(new GrantMethodSerialization { BelongModule = "#", Functions = new string[] { "#" } });
        }

        foreach (var (type, methods) in metadata.GrantMethods)
        {
            if (metadata.RejectMethods.ContainsKey(type))
            {
                seria.Functions.Add(new GrantMethodSerialization
                {
                    BelongModule = NameSelector(type),
                    Functions = methods
                        .Except(metadata.RejectMethods[type])
                        .Select(NameSelector)
                });
            }
            else
            {
                seria.Functions.Add(new GrantMethodSerialization
                {
                    BelongModule = NameSelector(type),
                    Functions = methods.Select(NameSelector)
                });
            }
        }

        seria.RejectModules = metadata.RejectClasses.Select(NameSelector);

        foreach (var (type, methods) in metadata.RejectMethods)
        {
            var target = seria.Functions.FirstOrDefault(n => n.BelongModule == NameSelector(type));

            if (target is not null)
            {
                target.RejectFunctions = target.RejectFunctions.Union(methods.Select(NameSelector));
            }
            else
            {
                seria.Functions.Add(new GrantMethodSerialization
                {
                    BelongModule = NameSelector(type),
                    RejectFunctions = methods.Select(NameSelector)
                });
            }
        }

        return seria;
    }

    public static GrantTypeSerialization WithRouters(this GrantTypeSerialization serialization, IEnumerable<string> routers)
    {
        serialization.Routers = routers;

        return serialization;
    }

    private static readonly Func<object, string> NameSelector = x =>
    {
        if (x is Type type)
        {
            var definition = type.GetCustomAttribute<PermisstionDefinitionAttribute>();

            return definition is null
                ? type.Name[..^10]
                : $"{type.Name[..^10]}:{definition.DisplayName}";
        }

        if (x is MethodInfo method)
        {
            var fullMethodName = $"{method.Name}({string.Join(',', method.GetParameters().Select(n => n.ParameterType.Name))})";
            var definition = method.GetCustomAttribute<PermisstionDefinitionAttribute>();

            return definition is null ? fullMethodName : $"{fullMethodName}:{definition.DisplayName}";
        }

        return string.Empty;
    };
}